#include "StdAfx.h"
#include "Player.h"

//===========================================================================
// Move
//===========================================================================

//===========================================================================
bool Move::Parse(
    char course
) {
    switch( course ) {
        case 'l' : case 'L' : this->course = LEFT;  break;
        case 'r' : case 'R' : this->course = RIGHT; break;
        default  : return false;
    }

    return true;
}

//===========================================================================
bool Move::Parse( 
    const char * command
) {
    if( !command || !*command )
        return false;

    stringstream stream( command );
    vector< string > commands;
    string val;
    while( getline( stream, val, ',' ) )
        commands.push_back( val );

    if( 3 != commands.size() )
        return false;

    if( !this->Parse( *commands[ 2 ].c_str() ) )
        return false;

    this->position.x = atoi( commands[ 0 ].c_str() );
    this->position.y = atoi( commands[ 1 ].c_str() );

    return true;
}

//===========================================================================
// Player
//===========================================================================

//===========================================================================
void CreatePlayers(
    IBoard *   board,
    IPlayer *& human,
    IPlayer *& ai
) {
    assert( board );

    human = new HumanPlayer( board, Piece::WHITE, ES_BOTTOM );
    ai    = new AiPlayer(    board, Piece::BLACK, ES_TOP    );
}

//===========================================================================
Player::Player(
    IBoard * board,
    Piece    piece,
    ESide    side
)   : m_board( board )
    , m_piece( piece )
    , m_side(  side  )
{
    switch( side ) {
        case ES_TOP    :
            this->m_forward =
            this->m_left    =  1;
            this->m_right   = -1;
            break;
        case ES_BOTTOM :
            this->m_forward =
            this->m_left    = -1;
            this->m_right   =  1;
            break;
        default :
            assert( false );
    }
}

//===========================================================================
Player::~Player( void )
{   
}

//===========================================================================
Piece Player::GetPieceType( void ) const
{
    return this->m_piece;
}

//===========================================================================
ESide Player::GetSide( void ) const
{
    return this->m_side;
}

//===========================================================================
bool Player::GenerateMove(
    const IRoute *      route,
          Move::ECourse course,
          IRoute *      appended /* = NULL */
) {
    assert( route );
    assert( Move::NONE != course );
    
    bool moved = false;
    for( ; ; ) {
        unsigned int moves = route->GetMoveCount();
        if( !moves )
            break;

        const Position * from = route->GetAt( 0 );
        Piece piece;
        if(   !this->m_board->GetPiece( from, piece )
            || this->m_piece != piece )
            break;
        
        bool firstmove = 1 == moves;
        if( !firstmove )
            from = route->GetAt( moves - 1 );

        int direction = this->GetCourse( course );
        Position middle( from->x + this->m_forward, from->y + direction );
        if( !this->m_board->GetPiece( &middle, piece ) )
            break;
        
        if( Piece::NONE == piece.type ) {
            if( firstmove )
                moved = true;
            if( appended ) {
                appended->Cleanup();
                appended->AppendMove( middle );
            }

            break;
        }

        if( this->GetPieceType() != !Piece( piece ) )
            break;
        if( !firstmove && !route->HasJumped() )
            break;

        Position dest( middle.x + this->m_forward, middle.y + direction );
        if( !this->m_board->GetPiece( &dest, piece ) )
            break;
        if( Piece::NONE != piece.type )
            break;

        if( appended ) {
            appended->Cleanup();
            appended->AppendMove( middle );
            appended->AppendMove( dest   );
        }
        
        moved = true;
        break;
    }

    return moved;
}

//===========================================================================
int Player::GetCourse(
    Move::ECourse course
) const {
    switch( course ) {
        case Move::LEFT  : return this->m_left;
        case Move::RIGHT : return this->m_right;
    }

    assert( false );
    return 0;
}

//===========================================================================
// HumanPlayer
//===========================================================================

//===========================================================================
HumanPlayer::HumanPlayer(
    IBoard * board,
    Piece    type,
    ESide    side
)   : Player( board, type, side )
{
}

//===========================================================================
HumanPlayer::~HumanPlayer( void )
{

}

//===========================================================================
void HumanPlayer::GetUserMove(
    Move & move,
    bool & abandon
) {
    abandon = false;

    bool firstmove = move.position == Position::INVALID_POSITION;
    const char * giveup  = "give up";
    const char * message = firstmove
                         ? "input your move ( x,y,[L|R] ) : "
                         : "input your move ( [L|R] ) : ";
    
    while( true ) {
        cout << message;

        string input;
        getline( cin, input );
        
        const char * command = input.c_str();
        if( !command || !*command )
            continue;        
        
        if( !strncmp( command, giveup, strlen( giveup ) ) ) {
            abandon = true;
            return;
        }

        if( firstmove ) {
            if( move.Parse( command ) )
                break;
        }
        else {
            if( move.Parse( *command ) )
                break;
        }
    }
}

//===========================================================================
bool HumanPlayer::OnTurn( void )
{
    IRoute * route    = CreateRoute( this->m_piece );
    IRoute * appended = CreateRoute( this->m_piece );
    bool     abandon  = false;
    Move     move;

    while( true ) {
        this->GetUserMove( move, abandon );
        if( abandon )
            break;

        route->AppendMove( move.position );
        if( !this->GenerateMove( route, move.course, appended ) ) {
            move.position = route->GetMoveCount() > 1
                          ? *route->GetDestination()
                          : Position::INVALID_POSITION;
            route->RewindLastMoves( 1 );

            continue;
        }
        route->AppendMove( appended );

        if( !route->HasJumped() )
            break;

        if(    this->GenerateMove( route, Move::LEFT  )
            || this->GenerateMove( route, Move::RIGHT ) ) {
            move.position = *route->GetDestination();
            route->RewindLastMoves( 1 );
            
            continue;
        }

        break;
    }

    this->m_board->Move( route );

    route->Release();
    appended->Release();

    return !abandon;
}

//===========================================================================
void HumanPlayer::Release( void )
{
    HumanPlayer::Release( this );
}

//===========================================================================
void HumanPlayer::Release(
    HumanPlayer * player
) {
    if( player )
        delete player;
}

//===========================================================================
// AiPlayer
//===========================================================================

//===========================================================================
AiPlayer::AiPlayer(
    IBoard * board,
    Piece    piece,
    ESide    side
)   : Player( board, piece, side )
{
    srand( static_cast< unsigned int >( time( NULL ) ) );
}

//===========================================================================
AiPlayer::~AiPlayer( void )
{

}

//===========================================================================
void AiPlayer::Release( void )
{
    AiPlayer::Release( this );
}

//===========================================================================
void AiPlayer::Release(
    AiPlayer * player
) {
    if( player )
        delete player;
}

//===========================================================================
bool AiPlayer::OnTurn( void )
{
    vector< IRoute * > routes;
    unsigned int count = this->FindAllDeepestRoutes( routes );
    if( !count )
        return false;

    const IRoute * route = routes[ rand() % count ];
    this->m_board->Move( route );

    this->ClearRoutes( routes );
    return true;
}

//===========================================================================
unsigned int AiPlayer::FindAllDeepestRoutes(
    vector< IRoute * > & routes
) {
    unsigned int size     = this->m_board->GetSize();
    unsigned int maxmove  = 2;
    IRoute *     route    = CreateRoute( this->m_piece );
    IRoute *     appended = CreateRoute( this->m_piece );
    
    for( unsigned int i = 0; i < size; i++ ) {
        for( unsigned int j = 0; j < size; j++ ) {
            Piece    piece;
            Position position( i, j );
            if( this->m_board->GetPiece( &position, piece ) ) {
                if( this->m_piece == piece ) {
                    route->Cleanup();
                    route->AppendMove( position );

                    this->FindAllDeepestRoutes(
                        route,
                        routes,
                        maxmove,
                        appended
                    );
                }
            }
        }
    }

    appended->Release();
    route->Release();

    return routes.size();
}

//===========================================================================
void AiPlayer::FindAllDeepestRoutes(
    IRoute *             route,
    vector< IRoute * > & routes,
    unsigned int &       maxmove,
    IRoute *             appended
) {
    assert( route );
    
    unsigned int moves = route->GetMoveCount();
    if( !moves )
        return;
    
    const unsigned int courses = 2;
    Move::ECourse course[ courses ] = {
        Move::LEFT, Move::RIGHT
    };

    bool added = false;
    for( unsigned int i = 0; i < courses; i++ ) {
        if( this->GenerateMove( route, course[ i ], appended ) ) {
            route->AppendMove( appended );
            this->FindAllDeepestRoutes(
                route,
                routes,
                maxmove,
                appended
            );
            
            route->RewindLastMoves(
                route->HasJumped() ? 2 : 1
            );
        }
        else if( !added ) {
            moves = route->GetMoveCount();
            if( moves >= maxmove ) {
                if( !routes.empty() ) {
                    IRoute * back = routes.back();
                    if( back->GetMoveCount() < moves )
                        this->ClearRoutes( routes );
                }

                routes.push_back( route->Clone() );
                added   = true;
                maxmove = moves;
            }
        }
    }
}

//===========================================================================
void AiPlayer::ClearRoutes(
    vector< IRoute * > & routes
) {
    auto it    = routes.begin();
    auto itEnd = routes.end();
    for( ; it != itEnd; it++ )
        ( *it )->Release();
    routes.clear();
}